package com.beiding.oa2;

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class AnalyzeUtils {

    private static AnalyzeObject create(Object o) {
        return new AnalyzeObject(o);
    }

    public static Collection<AnalyzeObject> scan(Object object) {

        //不能为null
        object.getClass();

        MappedObject mappedObject = new MappedObject();

        //创建并作为根节点
        AnalyzeObject analyzeObject = create(object);

        mappedObject.add(analyzeObject);

        //深度扫描
        scan(analyzeObject, mappedObject);


        return mappedObject.analyzeObjects();
    }

    private static void scan(AnalyzeObject parent, MappedObject mappedObject) {

        //如果是数组类型
        if (parent.isArray()) {

            if (parent.componentIsObject()) {
                Object[] arr = (Object[]) parent.getObject();
                for (int i = 0; i < arr.length; i++) {
                    handleComponent(parent, mappedObject, arr, i);
                }
            }

        } else {//如果是对象类型

            Object from = parent.getObject();

            for (Field field : parent.getObjectFieldMap().values()) {

                //对于非对象类型不处理
                if (Object.class.isAssignableFrom(field.getType())) {
                    handleField(parent, mappedObject, from, field);
                }
            }

        }

    }


    private static Package javaLang = Package.getPackage("java.lang");

    private static boolean isJavaLang(Class type) {
        if (type.getPackage() == null) {
            return false;
        }
        return type.getPackage().equals(javaLang);
    }

    //处理数组元素
    private static void handleComponent(AnalyzeObject parent, MappedObject mappedObject, Object[] arr, int index) {

        Object component = arr[index];


        if (component == null) {
            return;
        }


        //如果已经建立了映射关系就不再重复处理
        AnalyzeObject analyzeObject = mappedObject.get(component);
        if (analyzeObject != null) {
            analyzeObject.addHeld(parent, index);
            return;
        }

        analyzeObject = create(component);

        mappedObject.add(analyzeObject);

        analyzeObject.addHeld(parent, index);


        if (isJavaLang(component.getClass())) {
            return;
        }

        //递归扫描元素
        scan(analyzeObject, mappedObject);

    }


    private static void handleField(AnalyzeObject parent, MappedObject mappedObject, Object from, Field field) {
        Object value = null;
        try {
            value = field.get(from);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        if (value == null) {
            return;
        }

        AnalyzeObject analyzeObject = mappedObject.get(value);
        if (analyzeObject != null) {
            analyzeObject.addHeld(parent, field);
            return;
        }

        analyzeObject = create(value);

        mappedObject.add(analyzeObject);

        analyzeObject.addHeld(parent, field);

        if (isJavaLang(value.getClass())) {
            return;
        }

        scan(analyzeObject, mappedObject);
    }


    private static class MappedObject {
        private Map<Integer, AnalyzeObject> mappedObject = new HashMap<>();

        void add(AnalyzeObject analyzeObject) {
            mappedObject.put(System.identityHashCode(analyzeObject.getObject()), analyzeObject);
        }

        boolean contains(Object object) {
            return mappedObject.containsKey(System.identityHashCode(object));
        }

        AnalyzeObject get(Object o) {
            return mappedObject.get(System.identityHashCode(o));
        }

        Collection<AnalyzeObject> analyzeObjects() {
            return mappedObject.values();
        }

    }

    //-----------------对象创建-----------------
    private static Unsafe unsafe;

    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static <T> T allocateInstance(Class<T> tClass) {
        try {
            return (T) unsafe.allocateInstance(tClass);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        }
    }


}
